/*
 * Copyright (C) 2005 Jimi Xenidis <jimix@watson.ibm.com>, IBM Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 * $Id$
 */
/*
 * Code for initializing the 8259A PIC.
 *
 */

#include <types.h>
#include <lib.h>
#include <pgalloc.h>
#include <io.h>
#include <idt.h>
#include <xir.h>
#include <xirr.h>
#include <hype.h>

/* base addr of the PICs */
#define	PIC_MASTER	0x20
#define	PIC_SLAVE	0xA0

#define	CMD		0
#define	ISR		0
#define	IMR		1

/*
 * Interrupt vector (base) to be generated by the PICs
 * vector 0 through 31 (0x1f) are reserved by Intel
 */
#define IRQ_BASE	0x20

void
pic_mask_and_ack(uval irq)
{
	pic_disable(irq);
	if (irq < 8) {
		outb(PIC_MASTER + CMD, 0x60 + irq);
	} else {
		outb(PIC_SLAVE + CMD, 0x60 + (irq & 7));
		outb(PIC_MASTER + CMD, 0x60 + 2);
	}
}

void
pic_enable(uval irq)
{
	if (irq < 8)
		outb(PIC_MASTER + IMR, inb(PIC_MASTER + IMR) & ~(1 << irq));
	else
		outb(PIC_SLAVE + IMR,
		     inb(PIC_SLAVE + IMR) & ~(1 << (irq - 8)));
}

void
pic_disable(uval irq)
{
	if (irq < 8) {
		outb(PIC_MASTER + IMR, inb(PIC_MASTER + IMR) | (1 << irq));
	} else {
		outb(PIC_SLAVE + IMR, inb(PIC_SLAVE + IMR) | (1 << (irq - 8)));
	}
}

static sval
pic_eoi(struct cpu_thread *thread, xirr_t irq)
{
	struct mailbox *mbox = thread->mbox;

	if (mbox == NULL)
		return H_Parameter;

	if (irq > 0)
		pic_enable(irq);

	mbox->irq_masked &= ~(1 << irq);
	if (mbox->irq_pending & ~mbox->irq_masked) {
#if 0
		if (mbox->irq_pending & 0xc000)
			breakpoint();
		hprintf("int pending on unmask %x %x %x\n",
			irq, mbox->irq_pending, ~mbox->irq_masked);
#endif
		thread->preempt = CT_PREEMPT_CEDE;
	}

	return H_Success;
}

void
pic_init(void)
{
	hprintf("PIC init\n");
	xir_init_class(XIRR_CLASS_HWDEV, NULL, NULL);
	xirr_classes[XIRR_CLASS_HWDEV].eoi_fn = pic_eoi;

	/* mask all interrupts */
	outb(PIC_MASTER + IMR, 0xFF);
	outb(PIC_SLAVE + IMR, 0xFF);

	/* setup master PIC */
	outb(PIC_MASTER + CMD, 0x11);	/* edge triggered, cascade, ICW4 */
	outb(PIC_MASTER + IMR, IRQ_BASE);	/* interrupt base */
	outb(PIC_MASTER + IMR, 1 << 2);	/* slave on channel 2 */
	outb(PIC_MASTER + IMR, 0x01);

	/* setup slave PIC */
	outb(PIC_SLAVE + CMD, 0x11);	/* edge triggered, cascade, ICW4 */
	outb(PIC_SLAVE + IMR, IRQ_BASE + 8);	/* interrupt base */
	outb(PIC_SLAVE + IMR, 0x02);	/* slave identity is 2 */
	outb(PIC_SLAVE + IMR, 0x01);

#if 1
	/* enable all interrupts */
	outb(PIC_MASTER + IMR, 0);
	outb(PIC_SLAVE + IMR, 0);
#else
	/* disable all interrupts */
	outb(PIC_MASTER + IMR, 0xFF);
	outb(PIC_SLAVE + IMR, 0xFF);
#endif
}

void
pic_set_owner(struct cpu_thread *owner)
{
	int x;

	for (x = 0; x < XIRR_DEVID_SZ; ++x) {
		xirr_t xirr = xirr_encode(x, XIRR_CLASS_HWDEV);

		xir_default_config(xirr, owner, NULL);
	}
}
